#pragma once
#include <iostream>
#include <cstring>
#include <functional>
#include "Log.hpp"
#include "ip_net_address.hpp"
#include "myThread.hpp"
#include "ThreadPool.hpp"

// OS
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>

const static uint16_t gport = 8888;
const static int gsockfd = -1;
const static int gblocklog = 8;

using task_t = std::function<void()>; // 任务类型

enum
{
    SOCK_ERR = 1,
    BIND_ERR,
    ACCEPT_ERR,
    READ_ERR,
    WRITE_ERR,
    LISTEN_ERR
};

class TcpServer
{
public:
    TcpServer(uint16_t port = gport)
        : _local_port(port), _sockfd(gsockfd), _isrunning(false)
    {
    }
    void InitServer()
    {
        // 1.打开socket / 网卡
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            LOG(FATAL, "socket fail");
            exit(SOCK_ERR);
        }
        LOG(INFO, "socket success");

        // 2.填充socket
        struct sockaddr_in local_socket;
        uint len = sizeof(local_socket);
        memset(&local_socket, 0, len); // 初始化
        local_socket.sin_family = AF_INET;
        local_socket.sin_port = htons(_local_port);
        local_socket.sin_addr.s_addr = INADDR_ANY;

        // 3.bind
        int re = ::bind(_sockfd, (struct sockaddr *)&local_socket, len);
        if (re < 0)
        {
            LOG(FATAL, "bind fail");
            exit(BIND_ERR);
        }
        LOG(INFO, "bind success");

        // 4.Listening 监视，等待随时建立连接
        // 设置成监听状态（老板模式），能待定/持续不断地获取连接
        // gblocklog 连接队列的长度
        if (::listen(_sockfd, gblocklog) < 0)
        {
            LOG(FATAL, "listen fail");
            exit(LISTEN_ERR);
        }
        LOG(INFO, "listen success");
    }
    class ThreadData
    {
    public:
        TcpServer *const _this;
        int _re_fd;
        InetAdress _inet_addr;

    public:
        ThreadData(TcpServer *const pthis, int re_fd, InetAdress inet_addr)
            : _this(pthis), _re_fd(re_fd), _inet_addr(inet_addr)
        {
        }
    };
    void Loop() // 圈/环/连接
    {
        // 5.接收连接,  可能会有多个客户端socket发来连接，所以要反复接收
        _isrunning = true;
        // signal(SIGCHLD, SIG_IGN);//捕捉并忽略
        while (_isrunning)
        {
            struct sockaddr_in cli_socket;
            uint len = sizeof(cli_socket);
            int re_fd = accept(_sockfd, (struct sockaddr *)&cli_socket, &len);
            if (re_fd < 0)
            {
                LOG(FATAL, "accept fail");
                exit(ACCEPT_ERR);
            }
            InetAdress inet_addr(cli_socket);
            LOG(INFO, "accept success, get a new link, addr:[%s]", inet_addr.Str().c_str());

            // 让服务器并发连接多个客户端
            // //方法一：
            // if (fork() == 0)
            // {
            //     // child
            //     close(_sockfd);
            //     if (fork() != 0)
            //         exit(-1);              // 子进程退出
            //     Service(re_fd, inet_addr); // OS收养的孙子进程执行
            //     exit(-1);
            // }
            // // father
            // close(re_fd);
            // //////// waitpid(0, nullptr, 0); // 阻塞等待

            // //方法二：忽略子进程退出的信号，免等待；signal(SIGCHLD, SIG_IGN);//捕捉并忽略
            // if (fork() == 0)
            // {
            //     // child
            //     close(_sockfd);
            //     Service(re_fd, inet_addr); // OS收养的孙子进程执行
            //     exit(-1);
            // }
            // // father
            // close(re_fd);

            // // 方法三：线程
            // pthread_t tid;
            // ThreadData* td = new ThreadData(this, re_fd, inet_addr);
            // pthread_create(&tid, nullptr, Route, td);
            // 方法四：线程池
            task_t t = std::bind(&TcpServer::Service, this, re_fd, inet_addr);
            ThreadPool<task_t>::GetInstance(10)->Enqueue(&t);
        }

        _isrunning = false;
    }
    static void *Route(void *args) // 线程运行函数
    {
        pthread_detach(pthread_self()); // pthread_self()==tid //脱离自己,免等待
        ThreadData *td = static_cast<ThreadData *>(args);//ThreadData *td = (ThreadData *)args;
        td->_this->Service(td->_re_fd, td->_inet_addr);
        delete td;
        return nullptr;
    }

    void Service(int re_fd, InetAdress inet_addr) // 服务
    {
        //_isrunning = true;

        // 长服务
        // while (_isrunning)
        while (true)
        {
            // 读
            char buffer[1024] = {0};
            int n = ::read(re_fd, buffer, sizeof(buffer));
            if (n > 0)
            {
                LOG(INFO, "read success, [%s]", inet_addr.Str().c_str());

                // 写
                buffer[n] = '\0';
                std::string message = "server return : ";
                message += buffer;
                int w_n = ::write(re_fd, message.c_str(), message.size());
                if (w_n < 0)
                {
                    LOG(FATAL, "write fail");
                    exit(WRITE_ERR);
                }
                LOG(INFO, "write success :[%s]", message.c_str());
            }
            else if (n == 0) // client quit
            {
                LOG(INFO, "client quit");
                break;
            }
            else
            {
                LOG(FATAL, "read fail");
                exit(READ_ERR);
            }
        }

        //_isrunning = false;//一个client退出，其它client还在运行，所以server不能退出，_isrunning不能改
    }

    ~TcpServer()
    {
        if (_sockfd > gsockfd)
            close(_local_port);
    }

private:
    uint16_t _local_port;
    int _sockfd;
    bool _isrunning;
};